"""
python3 错误和异常
"""

'''
错误
语法错误或者称之为解析错
'''

'''
异常
首先，执行try子句
如果没有异常发生，忽略except子句，try子句执行后结束
如果在执行try子句的过程中发生了异常，那么try子句余下的部分将被忽略
如果异常的类型和 except 之后的名称相符，那么对应的except子句将被执行。最后执行 try 语句之后的代码
如果一个异常没有与任何的except匹配，那么这个异常将会传递给上层的try中
最后一个except子句可以忽略异常的名称，它将被当作通配符使用
使用 raise 语句抛出一个指定的异常。它必须是一个异常的实例或者是异常的类（也就是 Exception 的子类）
try except 语句还有一个可选的else子句，如果使用这个子句，那么必须放在所有的except子句之后
这个子句将在try子句没有发生任何异常的时候执行
不管 try 子句里面有没有发生异常，finally 子句都会执行
'''
# 除零异常
try:
    5 / 0
except ZeroDivisionError:
    print('0不能作为除数')
else:
    print('因为有异常 else 不被执行')

# else
try:
    pass
except ZeroDivisionError:
    print('0不能作为除数')
else:
    print('因为无异常 else 会被执行')

# finally
'''
try:
    raise KeyboardInterrupt
finally:
    print('finally 会被执行')
'''

# 最后一个except子句可以忽略异常的名称，它将被当作通配符使用。你可以使用这种方法打印一个错误信息，然后再次把异常抛出。
import sys
try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

# 如果你只想知道这是否抛出了一个异常，并不想去处理它，那么一个简单的 raise 语句就可以再次把它抛出
'''
try:
    raise NameError('HiThere')
except NameError:
    print('An exception flew by!')
    raise
'''

# 复杂的异常处理语句
def divide(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("division by zero!")
    else:
        print("result is", result)
    finally:
        print("executing finally clause")
divide(2, 1)
divide(2, 0)

'''
自定义异常
异常应该继承自 Exception 类，或者直接继承，或者间接继承
当创建一个模块有可能抛出多种不同的异常时，一种通常的做法是为这个包建立一个基础异常类
然后基于这个基础类为不同的错误情况创建不同的子类
大多数的异常的名字都以"Error"结尾
'''
class MyError(Exception):
    def __init__(self, value):  # 类 Exception 默认的 __init__() 被覆盖
        self.value = value
    def __str__(self):
        return repr(self.value)

try:
    raise MyError(2*2)
except MyError as e:
    print('My exception occurred, value:', e.value)

'''
预定义的清理行为
关键词 with 语句就可以保证诸如文件之类的对象在使用完之后一定会正确的执行他的清理方法
'''
'''
with open("myfile.txt") as f:
    print(f, end="")
'''
